/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年5月30日
 */
package com.massyframework.beanskin.runtime.framework.installing;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;

import javax.script.ScriptException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.massyframework.beanskin.ModuleRuntimeException;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.util.IOUtils;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader.Builder;
import com.massyframework.beanskin.util.exceptioncollect.ExceptionCollectUtils;
import com.massyframework.beanskin.util.localstore.LocalStore;
import com.massyframework.beanskin.util.variables.StringVariableReplacer;

/**
 * 初始化参数解析
 * 
 * @author huangkh
 *
 */
public abstract class InitParamsModuleParser<B> extends SecureModuleParser<B> {

	private static final Logger LOG = LoggerFactory.getLogger(InitParamsModuleParser.class);

	protected static final String INITPARAMS = "init-params";
	protected static final String PARAMETER  = "parameter";

	protected static final String VALUE      = "value";
	
	
	private   static final String  FILENAME  =  "initParams.properties";

	/**
	 * 构造方法
	 * 
	 * @param kernel {@link Assembly},内核装配件
	 * @param localStore {@link LocalStore},本地存储
	 */
	public InitParamsModuleParser(Assembly kernel, LocalStore localStore) {
		super(kernel, localStore);
	}

	@Override
	protected final void doParseNode(Node node, Expression<String> expression, StringVariableReplacer replacer,
			B builder) {
		super.doParseNode(node, expression, replacer, builder);
		
		//先加载外置的配置参数
		Properties props = this.tryLoadExtenralParams();
		if (props != null) {
			Enumeration<Object> em = props.keys();
			while (em.hasMoreElements()) {
				String name = em.nextElement().toString();
				String value = props.getProperty(name);
				this.setParameter(name, value, expression, replacer, builder);
			}
		}
		
		if (node.getNodeName().equals(INITPARAMS)) {
			this.doParseInitParams(node.getChildNodes(), expression, replacer, builder);
		}
	}
	
	/**
	 * 尝试加载扩展初始化参数
	 * @return {@link Properties},可能返回null.
	 */
	protected Properties tryLoadExtenralParams() {
		LocalStore localStore = this.getLocalStore();		
		String name = EXTENDS.concat(File.separator).concat(FILENAME);
		Path path = localStore.getDeploymentDirectory(this.getModuleIdentifier()).resolve(name);
		File file = path.toFile();
		if (!file.exists()) {
			return null;
		}
		
		InputStream is = null;
		try {
			is = new FileInputStream(file);
			Properties result = new Properties();
			result.load(is);
			return result;
		}catch(Exception e) {
			if (LOG.isErrorEnabled()) {
				LOG.error("load file failed:" + file.getPath(),e.getMessage());
			}
			ExceptionCollectUtils.addException(new ModuleRuntimeException(this.getModuleIdentifier(), e));
			return null;
		}finally {
			IOUtils.close(is);
		}
	}
	
	/**
	 * 解析初始化参数集合
	 * 
	 * @param nodes      {@link NodeList}
	 * @param expression {@link Expression} 表达式
	 * @param replacer   {@link StringVariableReplacer},字符串变量替换器
	 * @param builder    {@link B}
	 */
	protected Expression<String> doParseInitParams(NodeList nodes, Expression<String> expression,
			StringVariableReplacer replacer, B builder) {
		int size = nodes.getLength();

		for (int i = 0; i < size; i++) {
			Node node = nodes.item(i);

			if (node.getNodeName().equals(PARAMETER)) {
				this.doParseInitParameter(node, expression, replacer, builder);
			}
		}

		this.setGenericParams(expression, builder);
		return expression;
	}

	/**
	 * 设置通用参数,优先级最低
	 * 
	 * @param expression {@link Expression},表达式
	 * @param builder    {@link B},构建器
	 */
	protected void setGenericParams(Expression<String> expression, B builder) {
		Map<String, String> genericParams = this.getSubParams(this.getKernel().getConfig(), "module.*");

		for (Map.Entry<String, String> entry : genericParams.entrySet()) {
			this.setParamter(entry.getKey(), entry.getValue(), builder);
		}
	}

	/**
	 * 解析初始化参数
	 * 
	 * @param node       {@link Node}
	 * @param expression {@link Expression}，表达式
	 * @param replacer   {@link StringVariableReplacer}, 字符串变量替换器
	 * @param builder    {@link Builder}
	 */
	protected void doParseInitParameter(Node node, Expression<String> expression, StringVariableReplacer replacer,
			B builder) {
		String name = null;
		String value = null;

		Node nameNode = node.getAttributes().getNamedItem(NAME);
		if (nameNode != null) {
			name = this.getTextContent(nameNode);
		}

		Node valueNode = node.getAttributes().getNamedItem(VALUE);
		if (valueNode != null) {
			value = this.getTextContent(valueNode);
		}

		this.setParameter(name, value, expression, replacer, builder);
	}

	/**
	 * 设置参数
	 * 
	 * @param name       {@link String},参数名
	 * @param value      {@link String},参数值
	 * @param expression {@link Expression}，表达式
	 * @param builder    {@link Builder}
	 */
	protected void setParameter(String name, String value, Expression<String> expression,
			StringVariableReplacer replacer, B builder) {
		try {
			if (StringUtils.indexOf(value, "${") != -1) {
				value = replacer.foundAndReplace(value);
			}

			int pos = StringUtils.indexOf(value, "#{");
			if (pos != -1) {
				if (pos != 0) {
					new ScriptException("valid expression: " + value);
				}
				if (!value.endsWith("}")) {
					new ScriptException("valid expression: " + value);
				}
				
				String exp = StringUtils.substring(value, 2, value.length() -1);
				value = expression.evaluate(exp);
			}

			if (this.setParamter(name, value, builder)) {
				replacer.addVariable(name, value);
				expression.setVariable(name, value);
			}
		} catch (ScriptException e) {
			ExceptionCollectUtils.addException(new ModuleRuntimeException(this.getModuleIdentifier(), e));
			if (LOG.isErrorEnabled()) {
				LOG.error(e.getMessage(), e);
			}
		}
	}

	@Override
	protected Expression<String> createExpression(B builder, StringVariableReplacer replacer) {
		Expression<String> result = super.createExpression(builder, replacer);

		// 特殊指定的参数,优先级别高
		Map<String, String> spec = this.getSubParams(this.getKernel().getConfig(),
				this.getModuleIdentifier().toIdentifier());

		if (!spec.isEmpty()) {
			for (Map.Entry<String, String> entry : spec.entrySet()) {
				this.setParameter(entry.getKey(), entry.getValue(), result, replacer, builder);
			}			
		}
		return result;
	}

	/**
	 * 设置初始化参数
	 * 
	 * @param name    {@link String},参数名
	 * @param value   {@link String}, 参数值
	 * @param builder {@link B},构建器
	 */
	protected abstract boolean setParamter(String name, String value, B builder);

	

}
